package net.groupme.controller;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import net.groupme.exceptions.GameAlreadyExistingForThisUsernameException;
import net.groupme.exceptions.GameNotFoundException;
import net.groupme.exceptions.InsertFailedException;
import net.groupme.exceptions.UpdateFailedException;
import net.groupme.game.GAME_CONSTANTS;
import net.groupme.game.GameManager;
import net.groupme.game.GroupMeGame;
import net.groupme.game.persistence.GamePersistence;
import net.groupme.gui.constants.VIEW_VOCABULARY;
import net.groupme.model.Group;
import net.groupme.model.User;
import net.groupme.user.UserManager;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.Controller;
import org.springframework.web.servlet.mvc.SimpleFormController;


/**
 * created on 28/06/2007 
 * 
 * This GroupMe! controller handles requests for games.
 * 
 * TODO: Normally we should simply extend {@link SimpleFormController}...
 * 
 * @author Mischa Frank, <a href="mailto:code@planetfrank.net">code@planetfrank.net</a>
 * @author last edited by $Author: fabian $
 * 
 * @version $Revision: 1.10 $ $Date: 2008-03-04 17:06:26 $
 */
public class GameController implements Controller, ApplicationContextAware{

	/** Logger for this class and subclasses */
	protected final Log logger = LogFactory.getLog(getClass());
	
	
	/** the application context */
	ApplicationContext applicationContext = null;
	
	/**
	 * Set the ApplicationContext that this object runs in. 
	 * @param applicationContext the current {@link ApplicationContext} 
	 */
	public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
		this.applicationContext = applicationContext;
	}

	/* (non-Javadoc)
	 * @see org.springframework.web.servlet.mvc.Controller#handleRequest(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
	 */
	public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
		String userRequest = request.getRequestURI();
    	userRequest = userRequest.substring(userRequest.lastIndexOf("/") + 1);
    	
		logger.info("Requested Site/Action: " + userRequest);

    	//create the MaV:
    	ModelAndView mav = new ModelAndView();
    	
    	//switch cases that can be handled by this controller:
    	if(VIEW_VOCABULARY.ACTION_JOIN_GAME_FROM_QUEUE.equals(userRequest)){
    		mav = joinGame(request, response);
    	}
    	else if(VIEW_VOCABULARY.ACTION_CREATE_OWN_GAME.equals(userRequest)){
    		mav = createGame(request, response);
    	}
    	
    	else if(VIEW_VOCABULARY.ACTION_START_GAME.equals(userRequest)){
    		mav = startGame(request, response);
    	}
    	
    	else if(VIEW_VOCABULARY.ACTION_VIEW_GAMERESULTS.equals(userRequest)){
    		mav = viewResults(request, response);
    	}
    	
		return mav;
	}
	
	/**
	 * Initiates the Start of a GroupMeGame.
	 * 
	 * @param request the HTTP request
	 * @param response the HTTP response
	 * 
	 * @return the Spring {@link ModelAndView} object which includes the indicator for the <i>game playing view</i>
	 */
	private ModelAndView joinGame(HttpServletRequest request, HttpServletResponse response){
		String gameId = getStringValue(request, VIEW_VOCABULARY.PARAMETER_GAME_ID);
		
		ModelAndView mav = new ModelAndView();   
		UserManager userManager = (UserManager) applicationContext.getBean(VIEW_VOCABULARY.USER_MANGER, UserManager.class);
		mav.addObject(VIEW_VOCABULARY.PARAMETER_GAME_ID, new Long(gameId));
		mav.addObject(VIEW_VOCABULARY.PARAMETER_GAME_PLAYER, GAME_CONSTANTS.PLAYER_OPPONENT);
		mav.setViewName(VIEW_VOCABULARY.VIEW_PLAY_A_GAME);
		GroupMeGame game = null;
		try {
			GameManager.getInstance().setGameRunning(new Long(gameId));
			game = GameManager.getInstance().getGameRunning(new Long(gameId));
			mav.addObject(VIEW_VOCABULARY.PARAMETER_GAME_TOPIC, game.getGametopic());
			game.setOpponent(userManager.getUser());
			
			//only now the group of the corresponding user is created (usually in this method only the opponent's group has to be set) :
			User user = userManager.getUser();
			Group group = GameManager.getInstance().createGamingGroup(game.getGametopic(), user);
			if(game.getCreator().equals(user)){
				game.setCreatorsGroup(group);
			}else{
				game.setOpponentsGroup(group);
			}
		} catch (NumberFormatException e) {
			logger.error(e.getMessage(), e);
			mav.setViewName(VIEW_VOCABULARY.VIEW_GENERAL_ERROR);
		} catch (GameNotFoundException e) {
			logger.error(e.getMessage());
			mav.setViewName(VIEW_VOCABULARY.VIEW_GENERAL_ERROR);
		}

		logger.info("Startet the game with id: " + gameId);
		return mav;
	}
	
	/**
	 * Creates a new GroupMe Game.
	 * 
	 * @param request the HTTP request
	 * @param response the HTTP response
	 * 
	 * @return the Spring {@link ModelAndView} object which includes the indicator for the <i>group view</i>
	 */
	private ModelAndView createGame(HttpServletRequest request, HttpServletResponse response){
		String gameTopic = getStringValue(request, VIEW_VOCABULARY.PARAMETER_GAME_TOPIC);
		
		ModelAndView mav = new ModelAndView();   
		UserManager userManager = (UserManager) applicationContext.getBean(VIEW_VOCABULARY.USER_MANGER, UserManager.class);
		GroupMeGame game = new GroupMeGame(userManager.getUser(), userManager.getUser().getUserName(), gameTopic);
		long id = game.getId();
		mav.addObject(VIEW_VOCABULARY.PARAMETER_GAME_ID, id);
		mav.addObject(VIEW_VOCABULARY.PARAMETER_GAME_TOPIC, game.getGametopic());
		mav.setViewName(VIEW_VOCABULARY.VIEW_WAITING_FOR_TEAM);
		try {
			GameManager.getInstance().addGameToQueue(game);
		} catch (GameAlreadyExistingForThisUsernameException e) {
			mav.addObject(VIEW_VOCABULARY.PARAMETER_GAME_CREATION_ERROR, "You have already an open game.");
			mav.setViewName(VIEW_VOCABULARY.VIEW_GAME_OVERVIEW);
		}
		logger.info("Created the game with id: " + id + " and added it into the queue.");
		return mav;
	}
	
	/**
	 * Starts a waiting GroupMe Game.
	 * 
	 * @param request the HTTP request
	 * @param response the HTTP response
	 * 
	 * @return the Spring {@link ModelAndView} object which includes the indicator for the <i>group view</i>
	 */
	private ModelAndView startGame(HttpServletRequest request, HttpServletResponse response){
		Long gameId = new Long(getStringValue(request, VIEW_VOCABULARY.PARAMETER_GAME_ID));
		ModelAndView mav = new ModelAndView();   
		GroupMeGame game;
		try {
			game = GameManager.getInstance().getGameRunning(gameId);
			String gameTopic = game.getGametopic();
			
			//only now the group of the corresponding user is created (usually in this method only the creator's group has to be set):
			User user = ((UserManager) applicationContext.getBean(VIEW_VOCABULARY.USER_MANGER, UserManager.class)).getUser();
			Group group = GameManager.getInstance().createGamingGroup(gameTopic, user);
			if(game.getCreator().equals(user)){
				game.setCreatorsGroup(group);
			}else{
				game.setOpponentsGroup(group);
			}
			
			mav.addObject(VIEW_VOCABULARY.PARAMETER_GAME_ID, gameId);
			mav.addObject(VIEW_VOCABULARY.PARAMETER_GAME_PLAYER, GAME_CONSTANTS.PLAYER_CREATOR);
			mav.addObject(VIEW_VOCABULARY.PARAMETER_GAME_TOPIC, gameTopic);
			mav.addObject(VIEW_VOCABULARY.PARAMETER_GROUP, group.getId());
			mav.setViewName(VIEW_VOCABULARY.VIEW_PLAY_A_GAME);
		} catch (NumberFormatException e1) {
			logger.error(e1.getMessage());
			e1.printStackTrace();
		} catch (GameNotFoundException e1) {
			mav.addObject(VIEW_VOCABULARY.PARAMETER_GAME_CREATION_ERROR, "The game was not found any longer.");
			mav.setViewName(VIEW_VOCABULARY.VIEW_GAME_OVERVIEW);
		}
		logger.info("Launched the game with id: " + gameId + ".");
		return mav;	
	}
	
	/**
	 * Results.
	 * 
	 * @param request the HTTP request
	 * @param response the HTTP response
	 * 
	 * @return the Spring {@link ModelAndView} object which includes the indicator for the <i>group view</i>
	 */
	private ModelAndView viewResults(HttpServletRequest request, HttpServletResponse response){
		Long gameId = new Long(getStringValue(request, VIEW_VOCABULARY.PARAMETER_GAME_ID));
		ModelAndView mav = new ModelAndView();   
		GroupMeGame game;
		try {
			if (GameManager.getInstance().isGameRunning(gameId)){
				GameManager.getInstance().setGameFinished(gameId);
			}
			game = GameManager.getInstance().getGameFinished(gameId);
			String gameTopic = game.getGametopic();
			mav.addObject(VIEW_VOCABULARY.PARAMETER_GAME_ID, gameId);
			mav.addObject(VIEW_VOCABULARY.PARAMETER_GAME_TOPIC, gameTopic);
			mav.addObject(VIEW_VOCABULARY.PARAMETER_GAME_CREATOR, game.getCreator().getUserName());
			mav.addObject(VIEW_VOCABULARY.PARAMETER_GAME_OPPONENT, game.getOpponent().getUserName());
			mav.addObject(VIEW_VOCABULARY.PARAMETER_GAME_SCORE_RESOURCES, game.getScoreForResources());
			mav.addObject(VIEW_VOCABULARY.PARAMETER_GAME_SCORE_TAGS, game.getScoreForTags());
			mav.addObject(VIEW_VOCABULARY.PARAMETER_GAME_SCORE_FINAL, game.getOverallScore());
			mav.setViewName(VIEW_VOCABULARY.VIEW_GAME_RESULTS);
			
			//try to save the game:
			if(!game.isGameStoredInDatabase()){
				game.setGameStoredInDatabase(true);
				Long idWithinDB = GamePersistence.store(game).getId();
				GameManager.getInstance().TEMP_ID_TO_DB_ID_MAP.put(gameId, idWithinDB);
			}
		} catch (NumberFormatException e1) {
			logger.error(e1.getMessage());
			e1.printStackTrace();
		} catch (GameNotFoundException e1) {
			mav.addObject(VIEW_VOCABULARY.PARAMETER_GAME_CREATION_ERROR, "The game was not found any longer.");
			mav.setViewName(VIEW_VOCABULARY.VIEW_GAME_OVERVIEW);
		} catch (InsertFailedException e) {
			logger.error(e);
			e.printStackTrace();
		} catch (UpdateFailedException e) {
			logger.error(e);
			e.printStackTrace();
		}
		return mav;
		
	}
	
	/**
     * Very small helper function to extract a given parameter or attribute from a HttpServletRequest.
     * @param request the {@link HttpServletRequest} object
     * @param parameter the parameter
     * @return the value of the parameter as a String
     */
    private String getStringValue(HttpServletRequest request, String parameter){
    	String value = request.getParameter(parameter);
    	if(value == null && request.getAttribute(parameter) != null){ //normally this is not accessed but we do our best to extract the value.. 
    		value = request.getAttribute(parameter).toString();
    	}
    	return value;
    }
    



}
